package gbench.sandbox.neo4j.draft;

import static gbench.common.tree.LittleTree.MFT;
import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.tree.LittleTree.SimpleRecord.REC2;
import java.time.LocalDateTime;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// import gbench.common.tree.LittleTree.Jdbc.JdbcConfig;
import gbench.commonApp.jdbc.Neo4jApp;
import org.junit.jupiter.api.Test;

/**
 * 二元关系 示例：这就是对关系型数据库的模式演练
 * @author gbench
 *
 */
public class MyFruitShop {
    
    @Test
    public void foo() {
        
//        Neo4jApp.updateAnnotaion(Neo4jApp.Database.class, JdbcConfig.class, 
//            "url", "jdbc:neo4j:bolt://localhost/mydb?nossl","user", "neo4j","password", "123456");
        final var neo4jApp = Neo4jApp.newInstance(); // 创建图的应用App
        
        // 关系/谓词与运算图：(关系的动词化特征就是谓词,可以表示判断)
        // 关系可以视为一种顶点之间的计算，即关系可以理解为一个以其内含顶点为参数的函数 即 我们可以把
        // {"x/y",REC("0#vlblk","X", "1#vlblk","Y")} 视为 f(x:X,y:Y)的运算结果。
        // {"x/y/z",REC("0#vlblk","X", "1#vlblk","Y","2#vlblk","Z")} 视为 f(x:X,y:Y,"Z")的运算结果。
        // 即{"formula",REC(变量符号,变量类型)}。由此，我们可以把关系即顶点序列理解成 一个表达式。所有关系与表达式（以顶点或是边为构成项)的结构 是等价的。关系即表达式：
        // 此时我们就把关系视为：语句、命令模版了，于是就有了一下的结构设计：关系谓词(顶点1，顶点2). 即 REC(关系谓词,顶点1,顶点2) 的谓词(命题)逻辑。即把关系视作谓词判断。
        // 于是我们形成了一种 类似于日语的 后置表达式的 文法 诸如:(张三,苹果)买了3斤。表示 :张三买了3斤苹果.这种文法结构。
        
        // 双顶点二元关系 模式
        final var purchaseProto = REC( "0#vlblk","User", "1#vlblk","Product", "elblk","Purchase",// 基本的买卖关系对象
            "createtime",LocalDateTime.now(),"shop","华联超市");// 买卖关系
        final var teachProto = REC( "0#vlblk","User", "1#vlblk","User", "elblk","Teach");// 基本的买卖关系对象
        final var profileProto = REC("0#vlblk","User");// 摘要信息
        
        // 构建数据关系图
        final var g = neo4jApp.graph(REC2( // 交易的事务记录
            
            // 采购关系
            "张三/苹果", purchaseProto.derive("quality",3,"unit","斤","0#friend","王五"),// 张三买了3斤苹果
            "张三/葡萄", purchaseProto.derive("quality",3,"unit","斤"),// 张三买了3斤葡萄
            "李四/鸭梨", purchaseProto.derive("quality",5,"unit","斤"), // 李四买了5斤鸭梨
            "李四/苹果", purchaseProto.derive("quality",5,"unit","斤"), // 李四买了5斤苹果
            "王五/西红柿", purchaseProto.derive("quality",5,"unit","斤"), // 王五买了5斤西红柿
            "王五/苹果", purchaseProto.derive("quality",5,"unit","斤"),// 王五买了5斤苹果
            "赵六/苹果", purchaseProto.derive("quality",5,"unit","斤"), // 赵六买了5斤苹果
            "赵六/葡萄", purchaseProto.derive("quality",5,"unit","斤"), // 赵六买了5斤葡萄
            "赵六/香蕉", purchaseProto.derive("quality",5,"unit","斤"), // 赵六买了5斤香蕉
            
             // 学习关系
            "张三/张三", teachProto.derive("subject","数学"), // 张三自学数学
            "李四/张三", teachProto.derive("subject","语文"), // 李四教张三语文
            
            //单节点边：即只有一个顶点的数据。可以认为一个顶点是一种特殊的边。
            "赵六", profileProto.derive("0#sex","男","$birth","1983-8-12","0#address","上海市长宁区法华镇路101弄11#201"), // 设置张三基本信息
            "张三", profileProto.derive("$sex","女","$birth","1980-8-12") // 设置赵六基本信息
            
        ));// 交易数据库
        
        System.out.println(g);
        
        final var edgeLbls = Stream.of(purchaseProto,teachProto,profileProto).map(e->e.str("elblk"))
            .filter(e->e!=null).collect(Collectors.joining("|")); // 提取所有的边的label
        if(System.currentTimeMillis()>0) Neo4jApp // 过滤标记
        .getJdbc().withTransaction(sess->{// 查看买卖关系
            sess.sqlexecute(MFT("match (a)-[e:{0}]->(b) delete a,e,b",edgeLbls));//删除表
            sess.sqlexecute(MFT("create {0}",g));// 创建数据表
            final var sql = MFT("match (a)-[e:{0}]->(b) return a,e,b",edgeLbls);// 查询表
            System.out.println(sql);
            var mm = sess.sql2records(sql);
            System.out.println(FMT(mm));
            sess.sqlexecute(MFT("match p = (a)-[e:{0}]->(b) foreach ( n in nodes(p) | set n.marked=TRUE )",edgeLbls));// 更新顶点数据:设置标记
        });// withTransaction
        
        // 产看边属性
        //g.getAttributeSets().forEach((k,v)->System.out.println(k+"-->"+v));
    }// foo
    
    /**
     * 同名的边信息：
     */
    @Test
    public void bar() {
        final var neo4jApp = Neo4jApp.newInstance(); // 创建图的应用App
        
        // 基本数据关系
        final var purchaseProto = REC( "0#vlblk","User", "1#vlblk","Product", "elblk","Purchase", // 基本关系结构 
            "createtime",LocalDateTime.now(),"shop","华联超市");// 购买关系 
        final var teachProto = REC( "0#vlblk","User", "1#vlblk","User", "elblk","Teach");// 教学关系
        final var profileProto = REC("0#vlblk","User");// 个人基本信息
        final var productProto = REC("0#vlblk","Product");// 产品信息
        
        neo4jApp.set_intialize_handler(g->{// 把交易代码也写入到边明路径中。
            final var p = g.getEdge_name_renderer();// 记录老的边名记录器
            g.setEdge_name_renderer(edge->p.apply(edge)+"$"+edge.attr("transcode",""));// 在边实体记录中增加transcode 
        },false);// 注释intialize_handler 采用增量覆盖的方式 即replace 参数使用false
        
        // 构建数据关系图 ，注意此处使用了REC2 以保证可以使用重名的键
        final var g = neo4jApp.graph( REC2( 
            
            // 双节点边,注意张三买了两次苹果,但是每次的交易码 transcode 不一样。通过交易码进行边的区分。 
            "张三/苹果", purchaseProto.derive("quality",3,"unit","斤","0#friend","王五","transcode",1),// 张三买了5斤苹果 ，张三有个朋友属性：王五
            "张三/苹果", purchaseProto.derive("quality",5,"unit","斤","0#friend","王五","transcode",2),// 张三买了5斤苹果 ，张三有个朋友属性：王五
            "张三/葡萄", purchaseProto.derive("quality",3,"unit","斤","transcode",1),// 张三买了3斤葡萄 
            "张三/张三", teachProto.derive("subject","数学"), // 张三自学数学     "李四/张三", teachProto.derive("subject","语文"), // 李四教张三语文     // 单节点边。 
            
             // 单顶点属性 :注意单节点边的属性，名称需要符合定性属性名的命名规范。例如:'$name','0#name' 或是 '苹果.price' 这样的式样。 这里的单引号'用于分隔，实际使用 不要输入。
            "张三", profileProto.derive("$sex","女","$birth","1980-8-12"), // 张三的属性数据
            "苹果", productProto.derive("$price",5.8,"$title","红富士苹果","$sku","F0001"),// 苹果的属性数据
            "葡萄", productProto.derive("$price",9.8,"$title","龙眼葡萄","$sku","F0002") // 葡萄的属性数据。
        )); // graph 
        
        // 打印关系图
        System.out.println(g);
        //g.getAttributeSets().forEach((k,v)->System.out.println(MFT("{0}--->{1}",k,v)));// 显示图属性
        
        final var edgeLbls = Stream.of(purchaseProto,teachProto,profileProto).map(e->e.str("elblk"))
            .filter(e->e!=null).collect(Collectors.joining("|")); // 提取所有的边的label
        if(System.currentTimeMillis()>0) Neo4jApp // 过滤标记
        .getJdbc().withTransaction(sess->{// 查看买卖关系
            sess.sqlexecute(MFT("match (a)-[e:{0}]->(b) delete a,e,b",edgeLbls));//删除表
            sess.sqlexecute(MFT("create {0}",g));// 创建数据表
            final var sql = MFT("match (a)-[e:{0}]->(b) return a,e,b",edgeLbls);// 查询表
            System.out.println(sql);
            var mm = sess.sql2records(sql);
            System.out.println(FMT(mm));
            sess.sqlexecute(MFT("match p = (a)-[e:{0}]->(b) foreach ( n in nodes(p) | set n.marked=TRUE )",edgeLbls));// 更新顶点数据:设置标记
        });// withTransaction
    }//bar

}
